package code.easy.refresh

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

@Composable
fun rememberRefreshState() = remember { RefreshState() }

/**
 * 默认指示器跟随内容下拉一起向下偏移，如需改成悬浮或者其他样式，可使用 BasicRefresh()
 *
 * @param enabledRefresh 是否启动下拉刷新
 * @param enabledContentOnRefresh 在刷新过程中是否可以点击content内容
 * @param refreshTriggerDistance 触发下拉刷新的滑动距离，通常传入refreshIndicator的内容高度即可
 * @param maxOffsetMultipleTrigger 下拉的最大距离是refreshTriggerDistance(触发下拉刷新的滑动距离)的多少倍
 * @param refreshAnimDuration 从松手到开始刷新的动画的时长
 * @param finishAnimDuration 从刷新完成到关闭的动画的时长,如果没有触发刷新则是从松手到关闭的动画时长
 * @param refreshIndicator state：刷新状态；offsetTop：下拉的距离
 */
@Composable
fun EasyRefresh(
    refreshState: RefreshState,
    refreshTriggerDistance: Dp,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier,
    enabledRefresh: Boolean = true,
    enabledContentOnRefresh: Boolean = false,
    maxOffsetMultipleTrigger: Float = 3.5f,
    refreshAnimDuration: Long = 300,
    finishAnimDuration: Long = 300,
    refreshIndicator: @Composable BoxScope.(state: RefreshState.State, offsetTop: Dp) -> Unit,
    content: @Composable () -> Unit,
) {
    BasicRefresh(
        refreshState = refreshState,
        onRefresh = onRefresh,
        modifier = modifier,
        enabledRefresh = enabledRefresh,
        enabledContentOnRefresh = enabledContentOnRefresh,
        refreshTriggerDistance = refreshTriggerDistance,
        maxOffsetMultipleTrigger = maxOffsetMultipleTrigger,
        refreshAnimDuration = refreshAnimDuration,
        finishAnimDuration = finishAnimDuration,
        refreshIndicator = { _state, _offsetTop, _content ->
            Box(modifier = Modifier.offset(y = _offsetTop)) {
                _content()
            }

            if (refreshState.state != RefreshState.State.Null) {
                Box(
                    modifier = Modifier
                        .height(_offsetTop)
                        .clipToBounds()
                        .fillMaxWidth(),
                    contentAlignment = Alignment.BottomCenter,
                ) {
                    refreshIndicator(_state, _offsetTop)
                }
            }
        },
        content = content
    )
}

/**
 * 需要你在refreshIndicator中调用content.invoke()才会显示出内容，否则content不会显示
 *
 * @param refreshTriggerDistance 触发下拉刷新的滑动距离，通常传入refreshIndicator的内容高度即可
 * @param enabledRefresh 是否启动下拉刷新
 * @param enabledContentOnRefresh 在刷新过程中是否可以点击content内容
 * @param maxOffsetMultipleTrigger 下拉的最大距离是refreshTriggerDistance(触发下拉刷新的滑动距离)的多少倍
 * @param refreshAnimDuration 从松手到开始刷新的动画的时长
 * @param finishAnimDuration 从刷新完成到关闭的动画的时长,如果没有触发刷新则是从松手到关闭的动画时长
 * @param refreshIndicator state：刷新状态；offsetTop：下拉的距离；content：内容，需要实现者手动调用content.invoke()来绘制出内容
 * @param content 需要你在refreshIndicator中调用content.invoke()才会显示出内容，否则content不会显示
 */
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun BasicRefresh(
    refreshState: RefreshState,
    refreshTriggerDistance: Dp,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier,
    enabledRefresh: Boolean = true,
    enabledContentOnRefresh: Boolean = false,
    maxOffsetMultipleTrigger: Float = 2.5f,
    refreshAnimDuration: Long = 300,
    finishAnimDuration: Long = 300,
    refreshIndicator: @Composable BoxScope.(state: RefreshState.State, offsetTop: Dp, content: @Composable () -> Unit) -> Unit,
    content: @Composable () -> Unit,
) {
    val refreshHeaderHeightPx = LocalDensity.current.run { refreshTriggerDistance.toPx() }
    val coroutineScope = rememberCoroutineScope()
    val nestedScrollConnection = remember(refreshState, coroutineScope) {
        RefreshNestedScrollConnection(refreshState, coroutineScope).apply {
            this.maxOffsetMultipleTrigger = maxOffsetMultipleTrigger
        }
    }.apply {
        refreshTrigger = refreshHeaderHeightPx
    }
    var mask by remember { mutableStateOf(false) }
    LaunchedEffect(key1 = refreshState.state) {
        when (refreshState.state) {
            RefreshState.State.Null -> {
                mask = false
            }
            RefreshState.State.Close, RefreshState.State.Finish -> {
                refreshState.animateOffsetTo(0f, finishAnimDuration)
                refreshState.state = RefreshState.State.Null
            }
            RefreshState.State.Refresh -> {
                mask = true
                refreshState.animateOffsetTo(refreshHeaderHeightPx, refreshAnimDuration)
                onRefresh()
            }
            else -> {
            }
        }
    }
    Box(
        modifier = Modifier
            .clipToBounds()
            .nestedScroll(connection = nestedScrollConnection)
            .then(modifier)
    ) {
        val offsetTop = if (!enabledRefresh) 0.dp else with(LocalDensity.current) { refreshState.indicatorOffset.toDp() }
        refreshIndicator(refreshState.state, offsetTop, content)
        // 禁止content滑动的遮罩
        if (mask && !enabledContentOnRefresh) {
            Box(Modifier.fillMaxSize().verticalScroll(rememberScrollState()))
        }
    }
}
